%{

#include <boost/lexical_cast.hpp>

#include <MAlice/MAliceDriver.hpp>

class MAliceDriver;

#define YYSTYPE  yy::MAliceParser::semantic_type
#define YYTOKEN  yy::MAliceParser::token_type

#ifdef yywrap
#       undef yywrap
#endif
#define yywrap() 1
#define YY_SKIP_YYWRAP

%}

/*** Flex Declarations and Options ***/

/* the manual says "somewhat more optimized" */
%option batch

/* enable scanner to generate debug output. disable this for release
 * versions. */
%option debug

/* no support for include files is planned */
%option noyywrap nounput

/* enables the use of start condition stacks */
%option stack

/* The following paragraph suffices to track locations accurately. Each time
 * yylex is invoked, the begin position is moved onto the END position. */
%{
#define YY_USER_ACTION  yylloc->columns(yyleng);
%}

%{ /*** C/C++ Declarations ***/

#include <string>

#include "MAliceScanner.hpp"

/* import the parser's token type into a local typedef */
typedef yy::MAliceParser::token      token;
typedef yy::MAliceParser::token_type token_type;

/* By default yylex returns int, we use token_type. Unfortunately yyterminate
 * by default returns 0, which is not of token_type. */
 
#ifdef yyterminate
# undef yyterminate
#endif
#define yyterminate() return token::END
%}

DIGIT  [0-9]
ID     [[:alpha:]\_]+[[:alnum:]\_]*
WS     [ \t]

%% /*** Regular Expressions Part ***/

 /* code to place at the beginning of yylex() */
%{
    // reset location
%}

 /*** lexer rules ***/


[ \t\r] /* gobble up white-spaces */ {
    yylloc->step();
}

[\n]+ /* gobble up END-of-lines */ {
    yylloc->lines(yyleng); yylloc->step();
}


 /******************************************************************************
 * L i t e r a l s
 ******************************************************************************/

{DIGIT}+ {
	yylval->i = boost::lexical_cast<int>(string(yytext, yyleng));
    return token::int_literal;
}

 /* Float Literals
{DIGIT}*\.?{DIGIT}+([eE][-+]?{DIGIT}+)? {
	yylval->d = boost::lexical_cast<double>(string(yytext, yyleng));
    return token::float_literal;
}
 */

\'[^']\' {

		yylval->c = yytext[1];
		
		if (yyleng < 3)
		{
			cerr << "'' is an empty character! Will be replace by \\0. Did you mean a string literal? Use \"\" to for string literal." << endl;
			yylval->c = '\0';
		}
		else if (yyleng > 3)
		{
			string s = string(yytext, yyleng);
			cerr << '\'' << s << '\'' <<  " is too large to fit into a character! Did you mean a string literal? Use \"";
			cerr << s << "\" to for string literal." << endl;
		}

		return token::char_literal;
}

\"[^"]*\" {		
    string s = "";
    
		for (int i = 1; i < yyleng - 1; i++)
		{
			if ( yytext[i] == '\\' )
			{
				switch( yytext[++i] )
				{
				case 'n' : s.append(1, '\n'); break;
				case 'r' : s.append(1, '\r'); break;
				case 't' : s.append(1, '\t'); break;
				case 'v' : s.append(1, '\v'); break;
				case 'b' : s.append(1, '\b'); break;
				case '\'': s.append(1, '\\'); break;
				default:
					cerr << "Unrecognised escape sequence '\\" << yytext[i] << "'" << endl;
					s.append(1, '\\');
					s.append(1, yytext[i]);
				}
			}
			else
			{
				s.append(1, yytext[i]);
			} 
		}

    yylval->s = new string(s);
    return token::string_literal;
}

"true" {
        yylval->b = true;
        return token::bool_literal;
}

"false" {
        yylval->b = false;
        return token::bool_literal;
}

"&&"				return token::logical_and;
"||"				return token::logical_or;
"<<"              return token::shift_left;
">>"              return token::shift_right;
"<="              return token::less_than_equal;
">="              return token::greater_than_equal;
"=="              return token::equal;
"!="              return token::not_equal;
"ate"             return token::kw_ate;
"drank"           return token::kw_drank;
"became"          return token::kw_became;

"letter"			return token::kw_letter;
"number"			return token::kw_number;
"sentence"			return token::kw_sentence;

"a"               return token::kw_a;
"so"              return token::kw_so;
"The"             return token::kw_the;
"piece"           return token::kw_piece;
"had"             return token::kw_had;
"Alice"           return token::kw_alice;

"and"             return token::kw_and;
"but"             return token::kw_but;
"was"             return token::kw_was;

"changed"         return token::kw_changed;
"contained"       return token::kw_contained;

"either"          return token::kw_either;

"eventually"      return token::kw_eventually;

"enough"{WS}+"times"  return token::kw_enough_times;


"room"            return token::kw_room;
"found"           return token::kw_found;


"because"         return token::kw_because;

"which"           return token::kw_which;
"what"            return token::kw_what;
"perhaps"         return token::kw_perhaps;


"or"		  return token::kw_or;
"maybe"           return token::kw_maybe;

"thought"         return token::kw_thought;
"then"            return token::kw_then;
"too"             return token::kw_too;

"said"            return token::kw_said;
"spider"          return token::kw_spider;
"spoke"				return token::kw_spoke;

"unsure"			return token::kw_unsure;

"Looking-Glass"		return token::kw_looking_glass;
"went"				return token::kw_went;
"through"			return token::kw_through;

"Alice"{WS}+"was"{WS}+"unsure"							return token::was_unsure;
"Alice"{WS}+"was"{WS}+"unsure"{WS}+"which"	return token::was_unsure;

{ID}"'s" {
	assert (yytext[yyleng - 2] == '\'');
	assert (yytext[yyleng - 1] == 's' );
	yylval->s = new string(yytext, yyleng - 2);
	return token::array_id;
}

{ID} {
	yylval->s = new string(yytext, yyleng);
	return token::id;
}

. /* pass all other characters up to parser */ {
	yylval->c = yytext[0];
	return static_cast<token_type>(yytext[0]);
}

<<EOF>> {
        return token::END;
}

%% /*** Additional Code ***/

